package app.models.product;

import app.Const;
import app.exceptions.AmountException;
import app.kit.CommonKit;
import app.kit.TypeKit;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import goja.Logger;
import goja.annotation.TableBind;
import goja.plugins.sqlinxml.SqlKit;
import goja.rapid.db.Model;
import goja.rapid.db.RequestParam;
import goja.tuples.Pair;
import org.joda.time.DateTime;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;

import static app.Const.HUNDRED;
import static app.constant.DictConstant.PROFIT_MONTH;
import static app.constant.DictConstant.PROFIT_ONCE;
import static app.constant.ProductConstant.DEL;
import static app.constant.ProductConstant.EMPTY;
import static app.constant.ProductConstant.END;
import static app.constant.ProductConstant.END_SALES;
import static app.constant.ProductConstant.FAIL;
import static app.constant.ProductConstant.FOUND;
import static app.constant.ProductConstant.ONLINE;

/**
 * <p>
 * The database rlp_product Model.
 * </p>
 *
 * @author sagyf yang
 * @version 1.0
 * @since JDK 1.6
 */
@TableBind(tableName = "rlp_product")
public class Product extends Model<Product> {


    /**
     * The public dao.
     */
    public static final Product dao = new Product();


    private static final long serialVersionUID = 3067365535412091577L;

    public boolean updateStatus(String set_val, String id) {
        int update = Db.update(SqlKit.sql("product.updateStatus"), set_val, id);
        return update > 0;
    }




    private static final String[] order_fields = new String[]{"", "last_update_time", Const.FIELD_TIME_LIMIT, Const.FIELD_PRICE};

    /**
     * 分组产品数据
     *
     * @param group      分组
     * @param page       页码
     * @param order_type 排序方式 1表示默认按照产品发布时间排序；2表示按照期限排序；3表示按照金额排序
     * @return 产品信息
     */
    public List<Product> findByGroup(int group, int page, int order_type) {
        final Pair<Integer, Integer> pair = CommonKit.pageOffset(page);
        final String temp_sql = SqlKit.sql("product.groupWithProducts");
        String sql = String.format(temp_sql, order_fields[order_type]);
        return find(sql, ONLINE, group, pair.getValue1(), pair.getValue0());
    }

    public Product findByAllInfo(int product_id) {
        return findFirst(SqlKit.sql("product.findByAllInfo"), product_id);
    }

    /**
     * web端产品信息
     * @param product_id 产品ID
     * @return 产品信息
     */
    public Product findByWebIntro(int product_id) {
        return findFirst(SqlKit.sql("product.findByWebIntro"), product_id);
    }


    /**
     * 获取所有可用的产品信息
     * @return 所有产品
     */
    public List<Product> findAllAvailable() {
        return find(SqlKit.sql("product.findAllAvailable"));
    }

    /**
     * 查询所有可用的产品 的 id 和 name
     * @return 产品id和name
     */
    public List<Product> findNameAndIdAvalible() {
        return find(SqlKit.sql("product.findNameAndIdAvalible"));
    }


    /*
    pc端查询理财产品
     */
    public List<Product> findForPcHome(){
        DateTime now = DateTime.now();
        final DateTime start_date = now.plusDays(-30).millisOfDay().withMinimumValue();
        final DateTime end_time = now.millisOfDay().withMaximumValue();

        return Product.dao.find(SqlKit.sql("product.findForHome"), start_date, end_time, ONLINE, EMPTY, END_SALES, FOUND, Const.YES);
    }

    /**
     * @param page       页码
     * @param sort_field  1＝收益率 2=到期日 3=项目本金
     * @param direction  排序方向
     * @param brand      品牌ID
     * @param timeType   期限区间 1: 0-10天, 2: 10-30天 3: 1-3个月 4: 3-6个月 5: 6-12个月
     * @param statusType 状态  1-上线 2-售罄 4-到期
     * @param pageSize   每页显示数量
     * @return 数据
     */
    public Page<Product> findByPageList(int page, int pageSize,
                                        String sort_field, RequestParam.Direction direction,
                                        int brand, int timeType, int statusType) {


        StringBuilder whereSql = new StringBuilder();
        List<Object> params = Lists.newArrayList();

        if (statusType > 0) {
            if (statusType == DEL  || statusType == FAIL || statusType > END) {
                Logger.error("非法的状态");
                return null;
            }
            whereSql.append(" p.status = ? ");
            params.add(statusType);
        } else {
            whereSql.append(" p.status in (?,?,?,?,?) ");
            params.add(ONLINE);
            params.add(EMPTY);
            params.add(END);
            params.add(FOUND);
            params.add(END_SALES);
        }

        if (brand > 0) {
            whereSql.append(" AND p.brand = ? ");
            params.add(brand);
        }

        // 1: 0-10天, 2: 10-30天 3: 1-3个月 4: 3-6个月 5: 6-12个月
        if (timeType > 0) {
            switch (timeType) {
                case 1:
                    whereSql.append(" AND (p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?) ");
                    params.add("day");
                    params.add(0);
                    params.add(10);
                    break;
                case 2:
                    whereSql.append(" AND (p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?) ");
                    params.add("day");
                    params.add(10);
                    params.add(30);
                    break;
                case 3:
                    whereSql.append(" AND ((p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?) OR (p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?)) ");
                    params.add("day");
                    params.add(30);
                    params.add(90);
                    params.add("month");
                    params.add(1);
                    params.add(3);
                    break;
                case 4:
                    whereSql.append(" AND ((p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?) OR (p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?)) ");
                    params.add("day");
                    params.add(90);
                    params.add(180);
                    params.add("month");
                    params.add(3);
                    params.add(6);
                    break;
                case 5:
                    whereSql.append(" AND ((p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?) OR (p.TIME_LIMIT_UNIT = ? AND p.TIME_LIMIT BETWEEN ? AND ?)) ");
                    params.add("day");
                    params.add(180);
                    params.add(365);
                    params.add("month");
                    params.add(6);
                    params.add(9);
                    break;
            }

        }
        String order_sql = " p.status ASC,p.begin_date DESC";
        if (!Strings.isNullOrEmpty(sort_field)) {
            order_sql = "p." + sort_field + " " + direction.toString();
        }
        final String count_sql = String.format(SqlKit.sql("product.pageListCount"), whereSql);
        Product count_prod = findFirst(count_sql, params.toArray());
        final long total = TypeKit.getLong(count_prod, Const.FIELD_CNT);
        if(total == 0){
            return new Page<Product>(Collections.EMPTY_LIST, page, pageSize, 0, 0);
        }

        final Pair<Integer, Integer> offsets = CommonKit.pageOffset(page, pageSize);
        params.add(offsets.getValue1());
        params.add(offsets.getValue0());

        String sql = String.format(SqlKit.sql("product.findByPageList"), whereSql, order_sql);
        List<Product> products = find(sql, params.toArray());

        int totalPage = (int)total / pageSize;
        if (total % pageSize != 0) {
            totalPage++;
        }

        return new Page<Product>(products, page, pageSize, totalPage, (int)total);

    }

    /**
     * 查询需要导出数据
     * @return
     */
    public List<Product> findExcelExport() {
        return find(SqlKit.sql("product.findExcelExport"));
    }


    public int findAllAvailableCount() {
        Product first = findFirst(SqlKit.sql("product.findAllAvailableCount"));
        return  TypeKit.getInt(first, "cnt");
    }


    /**
     * 购买的时候 发生的金额变化，对金额进行重新变化计划等。
     * 1- 计算投资金额： 投资金额:raised_mount=(数据库的投资金额 + 本次购买金额）
     * 2- 计算进度， 进度=(投资金额:raised_mount+本次金额)/项目本金:price
     * 3- 可投金额进算： 可投金额:remaining_amount= (项目本金:PRICE-投资金额:raised_mount)
     *
     * @param buy_amount 本次购买投资金额
     * @return 购买产品本身
     */
    public Product updateBuy(BigDecimal buy_amount) {
        // 项目本金
        final BigDecimal productPrice = getBigDecimal(Const.FIELD_PRICE);
        // 投资金额
        final BigDecimal raised_mount = getBigDecimal("raised_mount");

        final BigDecimal now_raised_mount = raised_mount.add(buy_amount);
        //  金额购买满足(表示售罄，也就是成交）
        if (productPrice.compareTo(now_raised_mount) <= 0) {
            set(Const.FIELD_STATUS, EMPTY);
            final DateTime now = DateTime.now();
            set("deal_time", now);
        }
        set("raised_mount", now_raised_mount);
        set("progress", now_raised_mount.divide(productPrice, 2, BigDecimal.ROUND_HALF_UP).multiply(HUNDRED));
        // 可投金额
        BigDecimal remaing_amount = productPrice.subtract(now_raised_mount);
        if(BigDecimal.ZERO.compareTo(remaing_amount) > 1){
            throw new AmountException("remaing_amount is must 0!");
        }
        set("remaining_amount", remaing_amount);
        return this;
    }


    /**
     * 退单打款后，更新相关产品进度信息
     * @param amount 退单金额
     * @return 退单产品
     */
    public Product cancelBuy(BigDecimal amount){
        final BigDecimal productPrice = getBigDecimal(Const.FIELD_PRICE);
        // 投资金额
        final BigDecimal raised_mount = getBigDecimal("raised_mount");
        final BigDecimal now_raised = raised_mount.subtract(amount);
        set("raised_mount", now_raised);
        // 可投金额
        final BigDecimal remaining_amount = getBigDecimal("remaining_amount");
        set("remaining_amount", remaining_amount.add(amount));
        set("progress", now_raised.divide(productPrice, 2, BigDecimal.ROUND_HALF_UP).multiply(HUNDRED));
        return this;
    }

    public int getCollectionMode() {
        return TypeKit.getInt(this, Const.FIELD_COLLECTION_MODE);
    }

    /**
     * 查询销售已经超过当前时间，收益模式为每月等额本息和一次性到期支付，产品状态为在线销售和售罄和已成立的产品。
     *
     * @return 销售时间已经超过当前时间的产品数据
     */
    public List<Product> findByCheckStatus(DateTime time) {
        time = time.millisOfDay().withMaximumValue();
        // 取得产品状态为 上线中、售罄、销售结束、成立 产品
        return find(SqlKit.sql("product.findByCheckStatus"), PROFIT_MONTH, PROFIT_ONCE, ONLINE, EMPTY, FOUND, END_SALES, time, time);
    }


    /**
     * 上架的产品是否有人购买
     */
    public boolean checkBuy(String productId){
        Product first = findFirst(SqlKit.sql("product.checkBuy"), productId);
        return TypeKit.getInt(first,Const.FIELD_CNT) > 0;
    }

    public double avgYieldByBrand(int brandId) {
        Product avg = findFirst(SqlKit.sql("product.avgYieldByBrand"), brandId, DEL);
        if (avg != null) {
            Number yield = avg.getNumber("yield");
            return yield == null ? 0 : yield.doubleValue();
        } else {
            return 0;
        }
    }

    public int buyersByBrand(int brandId) {
        Product buyers = findFirst(SqlKit.sql("product.buyersByBrand"), brandId, DEL);
        if (buyers != null) {
            Number buyer = buyers.getNumber("buyers");
            return buyer == null ? 0 : buyer.intValue();
        } else {
            return 0;
        }
    }

    public double amountByBrand(int brandId) {
        Product amount = findFirst(SqlKit.sql("product.amountByBrand"), brandId, DEL);
        if (amount != null) {
            Number buyer = amount.getNumber("amount");
            return buyer == null ? 0 : buyer.doubleValue();
        } else {
            return 0;
        }
    }


    public List<Product> brandProductChat() {
        return find(SqlKit.sql("product.brandProductChat"));
    }

    public List<Product> orgProductChat() {
        return find(SqlKit.sql("product.orgProductChat"));
    }

    public Product findByClearing(int type, int productId) {
        return findFirst(SqlKit.sql("clearing.stat.info"), type, productId);
    }
}